import { _decorator, Component, math, systemEvent, KeyCode, RigidBodyComponent, Touch, EventTouch, SystemEventType, EventKeyboard, Vec3, clamp, PhysicsSystem, director, Director, CameraComponent, Label } from "cc";
import GCmd from "../../../scripts/config/GCmd";
import GEvent from "../../../scripts/config/GEvent";
import EventDispatch from "../../../scripts/utils/EventDispatch";
import NetWork from "../../../scripts/network/NetWork";
import Utils from "../../../scripts/utils/utils";
const { ccclass, property, menu } = _decorator;

const v3_0 = new math.Vec3();
const v3_1 = new math.Vec3();
const v2_0 = new math.Vec2();

const lv3_0 = new math.Vec3();

const KEYCODE = {
    W: 'W'.charCodeAt(0),
    S: 'S'.charCodeAt(0),
    A: 'A'.charCodeAt(0),
    D: 'D'.charCodeAt(0),
    w: 'w'.charCodeAt(0),
    s: 's'.charCodeAt(0),
    a: 'a'.charCodeAt(0),
    d: 'd'.charCodeAt(0),
};

enum EKey {
    NONE = 0,
    W = 1 << 0,
    A = 1 << 1,
    S = 1 << 2,
    D = 1 << 3,
    SHIFT = 1 << 4,
}

@ccclass("RALL-A-BALL.ballcontrol")
@menu("demo/roll-a-ball/ballcontrol")
export class ballcontrol extends Component {

    @property({ slide: true, range: [1, 3, 0.01] })
    public readonly shiftScale = 2;

    @property({ type: CameraComponent })
    public mainCamera: CameraComponent = null;

    private _rigidBody: RigidBodyComponent = null!;

    private _key: number = EKey.NONE;

    private moveKey : number = EKey.NONE;

    username = '';

    labelBall = null;

    frameCount = 0;

    start () {
        this._rigidBody = this.getComponent(RigidBodyComponent)!;

        this.initEventListen();

        PhysicsSystem.instance.autoSimulation = false;
    }

    initEventListen () {
        EventDispatch.instance().on(GEvent.UPDATE_FRAME, this.onUpdateFrame, this);
    }

    onUpdateFrame(data)
    {
        this.frameCount++;

        this.updateBallMove(data);
        this.updateLabelBallPosition();

        this.postUpdate(0.33);
    }

    updateBallMove(data)
    {
        this.moveKey = data.moveData[this.username];

        if (this.moveKey & EKey.W) {
            v3_0.z = 1;
        }
        if (this.moveKey & EKey.S) {
            v3_0.z = -1;
        }
        if (this.moveKey & EKey.A) {
            v3_0.x = 1;
        }
        if (this.moveKey & EKey.D) {
            v3_0.x = -1;
        }
        // if (this.moveKey & EKey.SHIFT) {
        //     v3_0.multiplyScalar(10);
        // }
        v3_0.multiplyScalar(5);

        if (v3_0.z != 0 || v3_0.x != 0) {
            this._rigidBody.applyImpulse(v3_0);
            v3_0.set(0, 0, 0);

            this._rigidBody.getLinearVelocity(v3_1);
            // console.log(`${this.frameCount}, ${ data.frameCount } ${ this.moveKey }, ${v3_1.x},${v3_1.y},${v3_1.z}`);
            this.labelBall.getComponent(Label).string = `${this.username},${this.frameCount},${ data.frameCount },${ this.moveKey }\n${v3_1.x},${v3_1.y},${v3_1.z}`;
        }
    }

    updateLabelBallPosition () {
        this.node.getWorldPosition(lv3_0);
        lv3_0.y += 1.5;
        this.mainCamera.convertToUINode(lv3_0, this.labelBall.parent, lv3_0);
        this.labelBall.setPosition(lv3_0);
    }

    private _subStepCount = 0;
    private _accumulator = 0;
    postUpdate (deltaTime: number) {
        if (!PhysicsSystem.instance.physicsWorld) return;
        if (!PhysicsSystem.instance.enable) {
            PhysicsSystem.instance.physicsWorld.syncSceneToPhysics();
            return;
        }
        this._subStepCount = 0;
        this._accumulator += deltaTime;
        director.emit(Director.EVENT_BEFORE_PHYSICS);
        while (this._subStepCount < PhysicsSystem.instance.maxSubSteps) {
            if (this._accumulator >= PhysicsSystem.instance.fixedTimeStep) {
                PhysicsSystem.instance.physicsWorld.syncSceneToPhysics();
                PhysicsSystem.instance.physicsWorld.step(PhysicsSystem.instance.fixedTimeStep);
                PhysicsSystem.instance.physicsWorld.emitEvents();
                PhysicsSystem.instance.physicsWorld.syncAfterEvents();
                this._accumulator -= PhysicsSystem.instance.fixedTimeStep;
                this._subStepCount++;
            } else {
                PhysicsSystem.instance.physicsWorld.syncSceneToPhysics();
                break;
            }
        }
        director.emit(Director.EVENT_AFTER_PHYSICS);
    }

    /*
    update (dt: number) {
        if (this.moveKey & EKey.W) {
            v3_0.z = 1;
        }
        if (this.moveKey & EKey.S) {
            v3_0.z = -1;
        }
        if (this.moveKey & EKey.A) {
            v3_0.x = 1;
        }
        if (this.moveKey & EKey.D) {
            v3_0.x = -1;
        }
        if (this.moveKey & EKey.SHIFT) {
            v3_0.multiplyScalar(10);
        }

        if (v3_0.z != 0 || v3_0.x != 0) {
            this.frameCount++;

            this._rigidBody.applyImpulse(v3_0);
            v3_0.set(0, 0, 0);
            
            this._rigidBody.getLinearVelocity(v3_1);
            console.log(`${this.frameCount}, ${dt}, ${v3_1.x},${v3_1.y},${v3_1.z}`);

            // v3_1.x = clamp(v3_1.x, -10, 10);
            // v3_1.y = clamp(v3_1.y, -10, 10);
            // v3_1.z = clamp(v3_1.z, -10, 10);
            // this._rigidBody.setLinearVelocity(v3_1);

            // cannon.js测试位置
            // let bodyPosition = this._rigidBody._body._sharedBody.body.position;
            // console.log(`frameCount : ${ this.frameCount } , x: ${bodyPosition.x} , y: ${bodyPosition.y} , z: ${bodyPosition.z}`);
            // console.log(`${ this.frameCount } ：${bodyPosition.x},${bodyPosition.y},${bodyPosition.z}`);
        }
    }
    */

    onEnable () {
        let tName = Utils.myUsername;
        if (tName != this.username)
            return;

        systemEvent.on(SystemEventType.KEY_DOWN, this.onKeyDown, this);
        systemEvent.on(SystemEventType.KEY_UP, this.onKeyUp, this);

        systemEvent.on(SystemEventType.TOUCH_MOVE, this.onTouchMove, this);
        systemEvent.on(SystemEventType.TOUCH_END, this.onTouchEnd, this);
    }

    onDisable () {
        let tName = Utils.myUsername;
        if (tName != this.username)
            return;

        systemEvent.off(SystemEventType.KEY_DOWN, this.onKeyDown, this);
        systemEvent.off(SystemEventType.KEY_UP, this.onKeyUp, this);

        systemEvent.off(SystemEventType.TOUCH_MOVE, this.onTouchMove, this);
        systemEvent.off(SystemEventType.TOUCH_END, this.onTouchEnd, this);
    }

    onKeyDown (event: EventKeyboard) {
        if (event.keyCode == KEYCODE.w || event.keyCode == KEYCODE.W) {
            this._key |= EKey.W;
        } else if (event.keyCode === KEYCODE.s || event.keyCode === KEYCODE.S) {
            this._key |= EKey.S;
        } else if (event.keyCode === KEYCODE.a || event.keyCode === KEYCODE.A) {
            this._key |= EKey.A;
        } else if (event.keyCode === KEYCODE.d || event.keyCode === KEYCODE.D) {
            this._key |= EKey.D;
        } else if (event.keyCode === KeyCode.SHIFT_LEFT ) {
            this._key |= EKey.SHIFT;
        }

        this.ballMove();
    }

    ballMove() {
        NetWork.sendDataToServer(this.node, GCmd.ClientCmd.BALL_MOVE, { username: this.username, key:this._key });
    }

    onKeyUp (event: EventKeyboard) {
        if (event.keyCode == KEYCODE.w || event.keyCode == KEYCODE.W) {
            this._key &= ~EKey.W;
        } else if (event.keyCode === KEYCODE.s || event.keyCode === KEYCODE.S) {
            this._key &= ~EKey.S;
        } else if (event.keyCode === KEYCODE.a || event.keyCode === KEYCODE.A) {
            this._key &= ~EKey.A;
        } else if (event.keyCode === KEYCODE.d || event.keyCode === KEYCODE.D) {
            this._key &= ~EKey.D;
        } else if (event.keyCode === KeyCode.SHIFT_LEFT ) {
            this._key &= ~EKey.SHIFT;
        }

        this.ballMove();
    }

    onTouchMove (touch: Touch, event: EventTouch) {
        touch.getDelta(v2_0);
        if (v2_0.x > 2) {
            this._key |= EKey.D;
            this._key &= ~EKey.A;
        } else if (v2_0.x < -2) {
            this._key |= EKey.A;
            this._key &= ~EKey.D;
        }
        if (v2_0.y > 2) {
            this._key |= EKey.W;
            this._key &= ~EKey.S;
        } else if (v2_0.y < -2) {
            this._key |= EKey.S;
            this._key &= ~EKey.W;
        }

        this.ballMove();
    }

    onTouchEnd (touch: Touch, event: EventTouch) {
        this._key = EKey.NONE;

        this.ballMove();
    }
}
